﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using CarBooker.Infrastructure.DomainBase;
using System.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;
using CarBooker.Infrastructure.EntityFactoryFramework;
using System.Text;

namespace CarBooker.Infrastructure.Repositories
{
    public abstract class SqlRepositoryBase<T>: RepositoryBase<T>
        where T:EntityBase
    {
        public delegate void AppendChildData(T entityAggregate,
            object chilEntityKeyValue);
        private Database database;
        private IEntityFactory<T> entityFactory;
        private Dictionary<string, AppendChildData> childCallbacks;
        private string baseQuery;
        private string baseWhereClause;

        protected SqlRepositoryBase()
            : this(null)
        { 
        }
        protected SqlRepositoryBase(IUnitOfWork unitOfWork)
            : base(unitOfWork)
        {
            this.database = DatabaseFactory.CreateDatabase();
            this.entityFactory = EntityFactoryBuilder.BuildFactory<T>();
            this.childCallbacks = new Dictionary<string, SqlRepositoryBase<T>.AppendChildData>();
            this.BuildChildCallbacks();
            this.baseQuery = this.GetBaseQuery();
            this.baseWhereClause = this.GetBaseWhereClause();

        }
        protected abstract void BuildChildCallbacks();
        public override T FindBy(object key)
        {
            StringBuilder builder = this.GetBaseQueryBuilder();
            builder.Append(this.BuildBaseWhereClause(key));
            return this.BuildEntityFromSql(builder.ToString());

        }

        protected abstract override void PersistNewItem(T item);
        protected abstract override void PersistUpdatedItem(T item);
        protected abstract override void PersistDeletedItem(T item);

        protected Database Database
        {
            get { return this.database; }
        }
        protected Dictionary<string, AppendChildData> ChildCallbacks
        {
            get { return this.childCallbacks; }
        }
        protected IDataReader ExecuteReader(string sql)
        {
            DbCommand command = this.database.GetSqlStringCommand(sql);
            return this.database.ExecuteReader(command);
        }
        protected virtual T BuildEntityFromSql(string sql)
        {
            T entity = default(T);
            using (IDataReader reader = this.ExecuteReader(sql))
            {
                entity = this.BuildEntityFromReader(reader);
            }
            return entity;
        }
        protected virtual T BuildEntityFromReader(IDataReader reader)
        {
            T entity = this.entityFactory.BuildEntity(reader);
            if(this.childCallbacks!=null && this.childCallbacks.Count>0)
            {
                object childKeyValue = null;
                DataTable columnData = reader.GetSchemaTable();
                foreach (string childKeyName in this.childCallbacks.Keys)
                {
                    if (DataHelper.ReaderContainsColumnName(columnData, childKeyName))
                    {
                        childKeyValue = reader[childKeyName];
                    }
                    else
                    {
                        childKeyValue = null;
                    }
                    this.childCallbacks[childKeyName](entity, childKeyValue);
                }
            }
            return entity;
        }
        protected virtual List<T> BuildEntitiesFromSql(string sql)
        {
            List<T> entities = new List<T>();
            using (IDataReader reader = this.ExecuteReader(sql))
            {
                while(reader.Read())
                {
                    entities.Add(this.BuildEntityFromReader(reader));
                }
            }
            return entities;
        }

        public override IList<T> FindAll()
        {
            StringBuilder builder = this.GetBaseQueryBuilder();
            builder.Append(";");
            return this.BuildEntitiesFromSql(builder.ToString());
        }
        protected abstract StringBuilder GetBaseQueryBuilder();
        protected abstract string GetBaseQuery();
        protected abstract string GetBaseWhereClause();
        protected virtual string BuildBaseWhereClause(object key) 
        {
            return string.Format(this.GetBaseWhereClause(), key);
        }



    }
}
